#pragma once

#include <iostream>
#include <fstream>
#include <string>
#include <unistd.h>
#include <stdarg.h>
#include <string.h>
#include "LockGuard.hpp"

#define SRCEEN_TYPE 1
#define FILE_TYPE 2

namespace log_ns
{
    pthread_mutex_t _log_mutex = PTHREAD_MUTEX_INITIALIZER;

    enum
    {
        DEBUG = 1,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };

    class logmessage
    {
    public:
        std::string _level;
        pid_t _id;
        std::string _filename;
        int _filenum;
        std::string _curr_time;
        std::string _message_info;
    };

    std::string GetCurrTime()
    {
        time_t now = time(nullptr);
        struct tm *curr_time = localtime(&now);
        char buffer[256];
        snprintf(buffer, sizeof(buffer), "%d-%d-%d %d:%d:%d",
                 curr_time->tm_year + 1990, curr_time->tm_mon + 1,
                 curr_time->tm_mday, curr_time->tm_hour,
                 curr_time->tm_min, curr_time->tm_sec);

        return buffer;
    }

    std::string LevelToString(int level)
    {
        switch (level)
        {
        case DEBUG:
            return "DEBUG";
        case INFO:
            return "INFO";
        case WARNING:
            return "WARNING";
        case ERROR:
            return "ERROR";
        case FATAL:
            return "FATAL";
        }
        return "UNKOWN";
    }

    class Log
    {
    public:
        Log(const std::string &filename) : _type(SRCEEN_TYPE), _filename(filename)
        {
        }
        void Enable(int type)
        {
            _type = type;
        }

        void FlushLogToSrceen(const logmessage &lg)
        {
            printf("[%s][%s][%d][%d][%s]: %s",
                   lg._level.c_str(), lg._filename.c_str(),
                   lg._filenum, lg._id,
                   lg._curr_time.c_str(), lg._message_info.c_str());
        }

        void FlushLogTOoFILE(const logmessage &lg)
        {
            std::ofstream out(_filename, std::ios::app);
            if (!out.is_open())
                return;
            char logtxt[2048];
            snprintf(logtxt, sizeof(logtxt), "[%s][%s][%d][%d][%s]: %s",
                     lg._level.c_str(), lg._filename.c_str(),
                     lg._filenum, lg._id,
                     lg._curr_time.c_str(), lg._message_info.c_str());
            out.write(logtxt, strlen(logtxt));
            out.close();
        }

        void FlushLog(const logmessage &lg)
        {
            LockGuard lock(&_log_mutex);
            if (_type == SRCEEN_TYPE)
                FlushLogToSrceen(lg);
            else
                FlushLogTOoFILE(lg);
        }

        void logMessage(const std::string &filename, const int filenum, const int level, const char *format, ...)
        {
            logmessage lg;
            lg._curr_time = GetCurrTime();
            lg._level = LevelToString(level);
            lg._filename = filename;
            lg._filenum = filenum;
            lg._id = getpid();

            va_list ap;
            va_start(ap, format);
            char log_info[1024];
            vsnprintf(log_info, sizeof(log_info), format, ap);
            va_end(ap);
            lg._message_info = log_info;

            FlushLog(lg);
        }

    private:
        int _type;
        std::string _filename;
    };

    Log lg("log.txt");

#define LOG(LEVEL, FORMAT, ...)                                          \
    do                                                                   \
    {                                                                    \
        lg.logMessage(__FILE__, __LINE__, LEVEL, FORMAT, ##__VA_ARGS__); \
    } while (0)

#define EnableSrceen()          \
    do                          \
    {                           \
        lg.Enable(SRCEEN_TYPE); \
    } while (0)

#define EnableFILE            \
    do                        \
    {                         \
        lg.Enable(FILE_TYPE); \
    } while (0)

}